<template>
  <el-upload
    class="avatar-uploader"
    ref="upload"
    :action="action"
    :accept="accept"
    :multiple="multiple"
    :limit="limit"
    :auto-upload="autoUpload"
    list-type="picture-card"
    :show-file-list="true"
    :file-list="fileList"
    :on-exceed="handleExceed"
    :on-remove="handleDelete"
    :on-preview="handlePictureCardPreview"
    :before-upload="handleBeforeUpload"
    :class="{ hide: fileList.length >= limit }"
  >
    <el-icon><Plus /></el-icon>
  </el-upload>
  <el-dialog v-model="dialogVisible" append-to-body>
    <img
      :src="dialogImageUrl"
      alt="Preview Image"
      style="display: block; max-width: 100%; margin: 0 auto"
    />
  </el-dialog>
</template>

<script lang="ts" setup>
  import { ref, watch } from 'vue'
  import { ElMessage } from 'element-plus'
  import { Plus } from '@element-plus/icons-vue'
  import type { UploadProps } from 'element-plus'
  import { uploadObject } from '@/utils/uploadFile.js'

  interface PropsType {
    files: any
    //  接受上传的文件类型
    accept?: string
    //  是否支持多选
    multiple?: boolean
    //  是否自动上传文件
    autoUpload?: boolean
    //	允许上传文件的最大数量
    limit?: number
  }
  const props = withDefaults(defineProps<PropsType>(), {
    accept: 'image/*',
    multiple: false,
    autoUpload: true,
    limit: 1
  })
  const emit = defineEmits(['update:files'])

  const dialogImageUrl = ref('')
  const dialogVisible = ref(false)
  const action = ref('')
  const uploadList = ref([])
  const number = ref(0)
  const fileList = ref([])
  const upload = ref(null)
  const baseUrl = ''

  watch(
    () => props.files,
    (val) => {
      if (val) {
        // 首先将值转为数组
        const list = Array.isArray(val) ? val : props.files.split(',')
        fileList.value = []
        // 然后将数组转为对象数组
        list.forEach((item: string) => {
          fileList.value.push({ name: item, url: item })
        })
      } else {
        fileList.value = []
        return []
      }
    },
    {
      immediate: true
    }
  )

  // 上传前的回调
  const handleBeforeUpload: UploadProps['beforeUpload'] = (file) => {
    try {
      uploadObject(file).then((res: any) => {
        uploadList.value.push({ name: res.name, url: res.url })
        number.value++
        uploadedSuccessfully()
      })
    } catch (error) {
      console.log('catch', error)
      ElMessage.error(error)
      upload.value.handleRemove(file)
    }
    return false
  }
  // 上传结束处理
  const uploadedSuccessfully = () => {
    if (number.value > 0 && uploadList.value.length === number.value) {
      fileList.value = fileList.value.concat(uploadList.value)
      uploadList.value = []
      number.value = 0
      emit('update:files', listToString(fileList.value))
    }
  }
  // 删除图片
  const handleDelete = (file: any) => {
    const index = fileList.value.map((f) => f.name).indexOf(file.name)
    if (index > -1) {
      fileList.value.splice(index, 1)
      emit('update:files', listToString(fileList.value))
    }
  }
  // 文件个数超出
  const handleExceed = () => {
    ElMessage.error(`上传文件数量不能超过 ${props.limit} 个!`)
  }
  // 预览
  const handlePictureCardPreview = (file: any) => {
    dialogImageUrl.value = file.url
    dialogVisible.value = true
  }
  // 对象转成指定字符串分隔
  function listToString(list: any, separator?: string) {
    let strS = ''
    separator = separator || ','
    for (let i in list) {
      if (list[i].url) {
        strS += list[i].url.replace(baseUrl, '') + separator
      }
    }
    return strS != '' ? strS.substring(0, strS.length - 1) : ''
  }
</script>

<style lang="scss" scoped>
  .avatar-uploader .avatar {
    width: 178px;
    height: 178px;
    display: block;
  }
</style>

<style lang="scss">
  .hide .el-upload--picture-card {
    display: none;
  }
  .avatar-uploader .el-upload {
    border: 1px dashed var(--el-border-color);
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: var(--el-transition-duration-fast);
  }

  .avatar-uploader .el-upload:hover {
    border-color: var(--el-color-primary);
  }

  .el-icon.avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    text-align: center;
  }
</style>
